/**
 * 
 */
package info.niwota.home.web;

import info.niwota.commons.io.Files;
import info.niwota.commons.web.AjaxRegistry;
import info.niwota.commons.web.PageCursor;
import info.niwota.home.R;
import info.niwota.webapp.acl.CryptoHelper;
import info.niwota.webapp.acl.ForbiddenException;
import info.niwota.webapp.web.PageContentProvider;
import instant.openvfs.home.Debug;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import jvs.vfs.ContentTypes;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.net.Uri;
import android.util.Log;

/**
 * This is the place for your custom logic:
 * 
 * 1. Identify the label, a label is a grouping of resources denoted by uris
 * such as folder, frame, ... Label can be embedded as component of the path or
 * as query parameters, checked against a permanent store, or invent your own
 * wheel. verify access permission with canAccess() and canShare()
 * 
 * 2. Set the cursor type and provide content. Two types are supported, inline
 * content and external file.
 * 
 * String s = "Hello world."; c.type = PageCursor.TYPE_CONTENT; c.content = s;
 * c.contentLength = s.length(); c.contentType = "text/html"; c.contentEncoding
 * = "UTF-8";
 * 
 * 
 * //save content to SD card if the requested resource is relatively large and
 * is not a file File f = getCacheFile(path); c.type = PageCursor.TYPE_URI;
 * c.contentLength = f.length(); c.uri = f.toURI().toString(); c.contentType =
 * "image/*";
 * 
 * A spectrum of frameworks are available out there for generating dynamic
 * content from complex (VM) to simple (MessageFormat). However I prefer
 * javascript/ajax over server side based templating approach. Android has
 * built-in support for JSONObject.
 * 
 * 3. (Optional) Grant access to individual uri with a hash value. Instead of
 * checking permission for every request, you may generate and append a hash
 * value to the requested uri. e.g.
 * 
 * String p = uri.getPath(); //get rid of query params to make sure we can later
 * generate the same hash value for checking. //the hash function adds a random
 * secret value to guard against attacks
 * 
 * String hash = CryptoHelper.hash(p, label, group, user, true); String query =
 * "?label=" + label + "&hash=" + hash;
 * 
 * 
 * 4. Test your app from a browser and have fun!
 * 
 * Li, Qiang
 * 
 */
public class WebappPageContentProvider extends PageContentProvider {
	private static final String TAG = "WebappPageContentProvider";

	@Override
	public boolean onCreate() {
		super.onCreate();
		//
		registerObjects();

		return true;
	}

	private void registerObjects() {
		final Context ctx = getContext();
		AjaxRegistry.getInstance().add(JvsFileObject.NAME,
				new JvsFileObject(ctx));
	}

	@Override
	protected void showMainPage(PageCursor c, Uri uri, int group, String user,
			String path) throws Exception {
		path = "/";
		uri = Uri.parse("v:" + path);
		final String label = Label.ROOT;
		checkAccess(uri, group, user, label);
		handleAsset(c, "main.tpl", TEXT_HTML);
	}

	@Override
	protected boolean showContent(PageCursor c, Uri uri, int group,
			String user, String path) throws Exception {
		if (Debug.DEBUG) {
			Log.d(TAG, "showContent path: " + path + " uri: " + uri);
		}
		if (path.startsWith("/web/") && path.endsWith(".htmf")) {
			handleAsset(c, path.substring(1), "text/html");
			return true;
		}

		if (path.equals("/bin/image")) {
			String img = uri.getQueryParameter("uri");
			handleImage(c, img, group, user);
			return true;
		}

		// only one root label for now
		final String label = Label.ROOT;

		checkAccess(uri, group, user, label);

		if (path.equals("/bin/load")) { // image
			// detail view
			String p = uri.getQueryParameter("uri");
			String s = load(c, label, new x.java.io.File("v:" + p), group, user);
			c.type = CONTENT;
			c.contentType = "text/javascript";
			c.content = s;
			c.contentLength = s.length();
			return true;
		}

		// JVS file
		if (path.equals("/bin/view")) {
			path = uri.getQueryParameter("path");

			x.java.io.File fi = new x.java.io.File("v:" + path);

			if (Debug.DEBUG) {
				Log.d(TAG, "handle file: " + fi + " exists: " + fi.exists());
			}

			if (!fi.exists()) {
				throw new NotFoundException(uri.toString());
			}
			if (fi.isFile() && !fi.canRead()) {
				throw new ForbiddenException(uri.toString());
			}
			
			// 
			c.title = fi.getDisplayName();
			c.lastModified = fi.lastModified();
			c.contentEncoding = fi.getContentEncoding();
			c.contentType = fi.getContentType();
			//
			if (c.contentType != null && c.contentType.equals(ContentTypes.APP_URI)) {
				c.type = REDIRECT;
				c.uri = fi.toRealURI().toString();
				return true;
			} else if (c.contentType != null && c.contentType.startsWith("text/")) {
				// assume text file are relatively small
				c.type = CONTENT;
				c.content = fi.getContent();
				c.contentLength = fi.length();
				//
				return true;
			} else if (c.contentType != null && c.contentType.equals(ContentTypes.APP_ANDROID)) {
				c.type = REDIRECT;
				c.uri = "/content/"+fi.getName();
				//
				return true;
			} else {
				c.type = URI;
				c.contentLength = fi.length();
				//cache if not native
				if (!fi.isNative()) {
					File f = cache(fi);
					c.contentLength = f.length();
					c.type = URI;
					c.uri = f.toURI().toString();
				} else {
					c.uri = fi.toRealURI().toString();
				}
				return true;
			}
		}
		//
		throw new NotFoundException(uri.toString());
	}

	@Override
	protected void handleImage(PageCursor c, String u, int group, String user)
			throws Exception {
		Uri uri = Uri.parse(u);
		if (Debug.DEBUG) {
			Log.d(TAG, "handleImage uri: " + uri);
		}

		checkHash(uri, group, user);
		//
		final String scheme = uri.getScheme();
		//
		if ("v".equals(scheme)) {
			x.java.io.File xf = new x.java.io.File(uri.getPath());
			if (xf.isNative()) {
				c.contentLength = xf.length();
				c.type = URI;
				c.uri = xf.toRealURI().toString();
				return;
			} else {
				// show android icon
				handleAsset(c, "image/android.png", "image/png");
				// showAssetImage(c, Uri.parse("assets://image/android.png"),
				// group, user, "image/android.png");
				return;
			}
		}
		//
		String path = null;
		if ("file".equals(scheme)) {
			path = uri.getPath();
		} else {
			// media store
			path = Files.getDataPath(getContext(), uri);
		}
		if (Debug.DEBUG) {
			Log.d(TAG, "uri file path: " + path);
		}
		File f = new File(path);
		if (!f.exists()) {
			throw new IOException("Uri path is not a file: " + uri);
		}
		c.contentLength = f.length();
		c.type = URI;
		c.uri = f.toURI().toString();
	}

	private File cache(x.java.io.File fi) throws Exception {
		// cache resource
		String path = fi.getPath();
		File f = getCacheFile(path);
		if (!f.exists()) {
			InputStream is = null;
			OutputStream os = null;
			try {
				is = fi.getInputStream();
				os = new java.io.FileOutputStream(f);
				copy(is, os);
			} finally {
				closeSilently(is);
				closeSilently(os);
			}
		}
		return f;
	}

	private static final String[] RES_NAMES = new String[] {
			"drawable/app.png", "drawable/audio.png", "drawable/back.png",
			"drawable/folder.png", "drawable/html.png", "drawable/image.png",
			"drawable/link.png", "drawable/text.png", "drawable/video.png",
			"drawable/filefolder.png", "drawable/top.png",
			"drawable/breadcrumb.png", "drawable/newfolder.png",
			"drawable/editor.png", "drawable/microphone.png",
			"drawable/camera.png", "drawable/cameravideo.png",
			"drawable/twister0", "drawable/twister1", "drawable/twister2", };

	private static final int[] RES_ICONS = new int[] { R.drawable.x_app,
			R.drawable.x_audio, R.drawable.x_back, R.drawable.x_folder,
			R.drawable.x_html, R.drawable.x_image, R.drawable.x_link,
			R.drawable.x_text, R.drawable.x_video, R.drawable.x_folder,
			R.drawable.ic_top, R.drawable.ic_bc32, R.drawable.x_folder,
			R.drawable.editor32, R.drawable.microphone32, R.drawable.camera32,
			R.drawable.cameravideo32, R.drawable.view_list32,
			R.drawable.view_thumbnail32, R.drawable.view_fling32, };

	protected int mapResource(String path) {
		int resid = -1;
		for (int i = 0; i < RES_NAMES.length; i++) {
			if (RES_NAMES[i].equals(path)) {
				resid = RES_ICONS[i];
				if (Debug.DEBUG) {
					Log.d(TAG, "mapResource name: " + path + " resid: "
									+ resid);
				}
				return resid;
			}
		}
		return super.mapResource(path);
	}

	private String load(PageCursor c, String label, x.java.io.File dir,
			int group, String user) throws Exception {
		StringBuilder sb = new StringBuilder();

		if (dir != null) {
			File[] uris = dir.listFiles();
			int size = uris.length;
			//
			sb.append("(function () {\r\nimageCount = " + size + ";\r\n");
			sb.append("imageUris = new Array();\r\n");
			if (size > 0) {
				for (int i = 0; i < size; i++) {
					File f = uris[i];
					String p = f.getPath();
					// attach the label to the uri for perm check later when the
					// uri is accessed
					String uri = f.toURI().toString();
					String hash = CryptoHelper
							.hash(p, label, group, user, true);
					String q = "?label=" + label + "&hash=" + hash;
					//
					sb.append("imageUris[" + i + "]=\"" + uri + q + "\";\r\n");
				}
			}
			sb.append("})();\r\n");
		}
		return sb.toString();
	}
}
